package com.onefish.toolkit.netty.chat;


import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyNettyServerHandler extends SimpleChannelInboundHandler<String> {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //群发通知
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    //每当从服务端读取到客户端写入的信息时，就将该信息转发给所有的客户端Channel（实现聊天室的效果）。
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String receiveMsg) throws Exception {
        Channel channel = ctx.channel();
        //遍历channelGroup，从而区分“我”和“别人”发出的消息，如果消息是自己发出的就显示“我”
        channelGroup.forEach(chnl -> {
            if (channel == chnl)
                chnl.writeAndFlush(nowTime() + " 【我】发送的消息：" + receiveMsg + "\n");
            else
                chnl.writeAndFlush(nowTime() + " 【" + SocketAddress(channel) + "】发送的消息：" + receiveMsg + "\n");
        });
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println(nowTime() + " 来自【客户端】-" + SocketAddress(ctx.channel()) + " 的消息读取完成");
        ctx.flush();
    }

    //连接建立。每当从服务端收到新的客户端连接时，就将新客户端的Channel加入ChannelGroup列表中，并告知列表中的其他客户端Channel
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 加入");
        channelGroup.writeAndFlush(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 加入\n");
        channelGroup.add(channel);
    }

    //连接断开。每当从服务端感知有客户端断开时，就将该客户端的Channel从ChannelGroup 列表中移除，并告知列表中的其他客户端Channel
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 离开");
        //会自动将channelGroup中断开的连接移除掉
        channelGroup.writeAndFlush(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 离开\n");
        channelGroup.remove(channel);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 注册");
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 取消注册");
    }

    //监听客户端上线
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 上线");
        ctx.fireChannelActive();
    }

    //监听客户端下线
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 下线");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(nowTime() + " 【客户端】-" + SocketAddress(channel) + " 连接异常");
    }


    public static String SocketAddress(Channel channel) {
        try {
            if (channel == null || channel.remoteAddress() == null) {
                System.out.println("地址为空");
                return "地址为空";
            }
            return channel.remoteAddress().toString().replace("/", "");
        } catch (Exception e) {
            e.printStackTrace();
            return "地址获取异常";
        }
    }

    public static String nowTime() {
        try {
            return sdf.format(new Date());
        } catch (Exception e) {
            e.printStackTrace();
            return "1970-01-01 00:00:00";
        }
    }
}

